#include "Cache.h"
#include <iostream>
using namespace std;


/* Concstructor for the cache does nothing */
Cache::Cache(){}

/* InsertBlock(uint blockAddr, char *block)
Checks to make sure that the cache is not full.  If it is full,
removes the least recently used block from the cache. Inserts a block into the cache 
(which is a map), adds the block to the least recently used tracker. 
blockAddr = the address of the block inserted, used for searching the
cache for that block
block = pointer to allocated memory for the block 
*/
void Cache::InsertBlock(uint blockAddr, char *block) {
	multimap <time_t, uint>::iterator lruit; // iterator for LRUinfo
	map <uint, char *>::iterator mit;	// iterator for cacheBlocks
	uint remblockAddr;			// address of removed block

	// Check to see if cache is full
	if (cacheBlocks.size() == MAXCACHESIZE) {
		// If full, find the least recently used block
		// and remove it from the cache.
		lruit = LRUinfo.begin();
		remblockAddr = lruit->second;
		mit = cacheBlocks.find(remblockAddr);
		free(mit->second);
		cacheBlocks.erase(mit);
		LRUinfo.erase(lruit);
	}
	// Insert the new block into the cache and the current time
	// into LRUinfo.
	cacheBlocks.insert(make_pair(blockAddr, block));
	LRUinfo.insert(make_pair(time(NULL), blockAddr));
}

/* FindBlock(uint blockAddr) 
Looks for a particular block address in the cache.  If in the cache,
it returns a pointer to the block.  Otherwise, it returns NULL.
blockAddr = the block address that will be looked for in the 
cache
*/
char* Cache::FindBlock(uint blockAddr) {
	// Look for block in cache
	map <uint, char *>::iterator mit = cacheBlocks.find(blockAddr);
	// Return NULL if not in cache
	if (mit == cacheBlocks.end()) {
		return NULL;
	}	
	// Return a pointer to the block contents if it is
	// in the cache
	else {
		return mit->second;
	}
}

/* TouchBlock(uint blockAddr)
Updates the access time of the block specified by block address  
blockAddr: block whose LRU time should be updated
*/
void Cache::TouchBlock(uint blockAddr) {
	multimap <time_t, uint>::iterator mit; // iterator for LRUinfo
	
	// Find the block address in the cache, erase old access
	// time information when found
	for (mit = LRUinfo.begin(); mit != LRUinfo.end(); mit++) {
		if (mit->second == blockAddr) {
			LRUinfo.erase(mit);
			break;
		}
	}
	// Insert new access time information
	LRUinfo.insert(make_pair(time(NULL), blockAddr));
}

/* CleanUp() 
Clears the cache.  This is only done when the file system is
remade.
*/
void Cache::CleanUp() {
	map <uint, char *>::iterator mit;	// iterator to cacheBlocks
	char *buff;				// pointer to blocks that will be freed

	// Iterate through cache contents and free associated blocks.
	for (mit = cacheBlocks.begin(); mit != cacheBlocks.end(); mit++) {
		buff = mit->second;
		free(buff);
	} 
	// Clear both cacheBlocks and LRUinfo
	cacheBlocks.clear();
	LRUinfo.clear();
	
}
